package com.clp.protocol.iec104.iec104.client;

import com.clp.protocol.iec104.iec104.apdu.Apdu;
import com.clp.protocol.iec104.iec104.apdu.IApdu;
import com.clp.protocol.iec104.iec104.apdu.apci.IApci;
import com.clp.protocol.iec104.iec104.apdu.apci.ICtrlArea;
import com.clp.protocol.iec104.iec104.apdu.apci.SCtrlArea;
import com.clp.protocol.iec104.iec104.apdu.asdu.IAsdu;
import com.clp.protocol.iec104.iec104.common.IncorrectRtuAddrException;
import com.clp.protocol.iec104.iec104.definition.ConstVal;
import com.clp.protocol.iec104.iec104.exception.IncorrectRecvSeqException;
import com.clp.protocol.iec104.iec104.exception.IncorrectSendSeqException;
import com.clp.protocol.iec104.iec104.exception.IncorrectSeqException;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class InMasterApduRecvHandler {
    private final InMaster inMaster;

    public InMasterApduRecvHandler(InMaster inMaster) {
        this.inMaster = inMaster;
    }

    public Apdu handleRecving(Apdu apdu) throws IncorrectRtuAddrException, IncorrectSeqException {
        // 1、检查 apdu
        checkRecvApduRtuAddr(apdu);
        checkRecvApduSeq(apdu);
        if (apdu.isSType()) {
            // 更新确认序号
            inMaster.setAckSeq(apdu.castToSType().getSApci().getCtrlArea().getRecvSeq());
        } else if (apdu.isIType()) {
            IApdu iApdu = apdu.castToIType();
            IApci iApci = iApdu.getIApci();
            IAsdu iAsdu = iApdu.getIAsdu();
            int sendSeq = iApci.getCtrlArea().getSendSeq();
            int recvSeq = iApci.getCtrlArea().getRecvSeq();
            // 处理接收序号
            inMaster.setRecvSeq((sendSeq + 1) % ConstVal.MAX_SEQ);
            // 处理确认序号
            inMaster.setAckSeq(recvSeq);

            // 处理累计确认
            inMaster.addCurrRecvCountCircularly();
            // 一定要返回确认帧的情况（重置计数）
            if (inMaster.needResetCurrRecvCountByRecving(apdu)) {
                inMaster.resetCurrRecvCount();
            }
            // 6、如果当前累计接收到达最大值，发送一个确认帧
            if (inMaster.needSendSApduOfAck()) {
                log.info("reach maxAckCount, send ack");
                inMaster.getApduSender().sendSApduOfAck();
            }

            // 公共地址不正确，给出提示
            int recvedRtuAddr = iAsdu.getRtuAddr();
            if (!(recvedRtuAddr == inMaster.rtuAddr())) {
                log.warn("Received an apdu with incorrect rtu address:" + recvedRtuAddr + " (rtu address:" + inMaster.rtuAddr() + ")");
//                    return;
            }
        }

        // 3、更新主站状态
        apdu = inMaster.stateInfo().updateStateByRecving(apdu);
        // 4、执行回调
        inMaster.getApduRecver().handleRecvCallbacks(apdu);
        return apdu;
    }

    protected void checkRecvApduRtuAddr(Apdu apdu) throws IncorrectRtuAddrException {
        if (!apdu.isIType()) return;
        int recvedRtuAddr = apdu.castToIType().getIAsdu().getRtuAddr();
        if (recvedRtuAddr != inMaster.rtuAddr()) {
            throw new IncorrectRtuAddrException(inMaster.rtuAddr(), recvedRtuAddr);
        }
    }

    protected void checkRecvApduSeq(Apdu apdu) throws IncorrectSeqException {
        if (apdu.isSType()) {
            SCtrlArea sControlArea = apdu.castToSType().getSApci().getCtrlArea();
            int remoteRecvSeq = sControlArea.getRecvSeq();
            if (remoteRecvSeq > inMaster.sendSeq()) {
                throw new IncorrectRecvSeqException(remoteRecvSeq);
            }
        } else if (apdu.isIType()) {
            ICtrlArea iControlArea = apdu.castToIType().getIApci().getCtrlArea();
            int remoteSendSeq = iControlArea.getSendSeq();
            if (remoteSendSeq != inMaster.recvSeq()) {
                throw new IncorrectSendSeqException(remoteSendSeq);
            }
        }
    }
}
